#pragma once
#include "KhaosVulkanConfig.h"

namespace Khaos
{
    class VulkanImageSub;
    class VulkanBufferView;
    class VulkanSharedBase;
    class VulkanDevice;

    // --------------------------------------------------------------------------------------------
    class VulkanBarrier : public AllocatedObject
    {
    public:
        enum QueueType { QueueNull, QueueGraphic, QueueCompute, QueueTransfer, QueueTypeMax };
        enum StageHint { StageHintDefault, StageHintRayTracing };
        enum OpType { OpNull, OpRelease, OpAcquire };

    public:
        void bindDevice(VulkanDevice* device) { m_device = device; }

        void clear();

        void addImageBarrier(VulkanImageSub* imageView, VkImageLayout oldLayout, VkImageLayout newLayout,
            QueueType oldQueue, QueueType newQueue, OpType op, StageHint oldStage, StageHint newStage);
        
        void addBufferBarrier(VulkanBufferView* bufferView, uint32_t oldStatus, uint32_t newStatus,
            QueueType oldQueue, QueueType newQueue, OpType op);

        void addBufferBarrierRaw(const VkBufferMemoryBarrier2& barrier, VulkanSharedBase* buffer);

        bool makeInfo(VkDependencyInfo& info) const;

        bool hasInfo() const { return m_barImages.size() || m_barBuffers.size(); }

        const std::vector<VulkanSharedBase*>& getViews() const { return m_views; }

#if _VkCmdListLogEnabled
        const char* _getDebugName(VkBuffer buffer) const;
#endif

    private:
        uint32_t _getQueueFamilyIndex(QueueType queue) const;

    private:
        VulkanDevice* m_device = nullptr;
        std::vector<VkImageMemoryBarrier2> m_barImages;
        std::vector<VkBufferMemoryBarrier2> m_barBuffers;
        std::vector<VulkanSharedBase*> m_views;

#if _VkCmdListLogEnabled
        std::map<VkBuffer, const char*> m_debugNames;
        mutable String m_debugNameBuffer;
#endif
    };

    // --------------------------------------------------------------------------------------------
    class VulkanSemaphore : public AllocatedObject
    {
    public:
        explicit VulkanSemaphore(VulkanDevice* device) : m_device(device) { create(); }
        ~VulkanSemaphore() { destroy(); }

        bool create();
        void destroy();

        void setUserData(uint32_t userData) { m_userData = userData; }
        uint32_t getUserData() const { return m_userData; }

        operator VkSemaphore() const { return m_semaphore; }
        VkSemaphore getHandle() const { return m_semaphore; }

    private:
        VulkanDevice* m_device;
        VkSemaphore m_semaphore = VK_NULL_HANDLE;
        uint32_t m_userData = 0;
    };
}

